package resources;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.ResourceBundle;

import resources.factories.MoleculeFactory;

import core.molecules.IMolecule;



/**
 * A library used to create any number of "factories" stored in the factorylibrary resource file
 * This library builds automatically and is statically available to all classes in this program
 * @author Julian
 *
 */
public class FactoryLibrary {

	//the driectory for the factory properties document. Do not change unless absoltuely necessary
	private static final String FACTORY_PROPERTIES = "resources.factories";
	
	//the resource bundle key associated with the directory of the factories
	private static final String FACTORY_DIR_KEY = "FACTORY_DIRECTORY";
	
	//the prefex for all keys associated with a factory type/location. This tag is essential for a factor to be added to the library
	private static final String FACTORY_PREFIX = "factory";
	
	public static ArrayList<MoleculeFactory<?>> myFactories;
	public static final char DELIMITER = '.';
	
	static{
		myFactories = new ArrayList<MoleculeFactory<?>>();
		ResourceBundle rb = ResourceBundle.getBundle(FACTORY_PROPERTIES);
		String factoryDir = rb.getString(FACTORY_DIR_KEY );
		for (String key: rb.keySet()){
			if (key.startsWith(FACTORY_PREFIX)){
				myFactories.add(createFactory(rb, factoryDir, key));
			}
		}
		
	}

	/**
	 * Creates a factory based on the resource bundle, The directory of the factory, 
	 * and the key of which a substring corresponds to the specific name of the factory class
	 * @param <T>
	 * @param rb
	 * @param factoryDir
	 * @param key
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private static <T extends IMolecule> MoleculeFactory<T> createFactory(ResourceBundle rb, String factoryDir,
			String key){
		try {
			return (MoleculeFactory<T>) Class.forName(factoryDir + DELIMITER + key.substring(key.lastIndexOf(DELIMITER)+1)).getConstructor(String.class, ResourceBundle.class).newInstance(rb.getString(key), rb);
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException();
		} 
	}
	
	/**
	 * Retrieves a factory from this library through reflection. If this library does not contain a factory of the input class
	 * type then null is returned
	 * @param <T>
	 * @param clazz
	 * @return
	 */
	public static <T extends MoleculeFactory> T getFactoryByClass(Class<T> clazz){
		for (MoleculeFactory<?> mf : myFactories){
			if (mf.getClass().isAssignableFrom(clazz))
				return (T) mf;
		}
		return null;
	}
	
	
	
}
